# -*- coding:utf-8 -*-
from __future__ import absolute_import
import logging
import re
import os
import json
import datetime
import traceback
from collections import namedtuple
from django.contrib.auth.models import User
from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
from django.utils import timezone
from django.db.models import F, Q
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
from obs import RestoreTier, SetObjectMetadataHeader, PutObjectHeader
from data_mgt.models import SceneData, DataDownloadInfo, ObsAsyncJobStatus, AnonymizedData, SceneCutRecord, \
    AsyncJobSceneCut, DataSetInfo, DataImportTask, SceneReviewRecord
from data_mgt.serializer import SceneDataSerializer, DataDownloadInfoSerializer, SceneReviewRecordSerializer
from data_mgt.tasks import async_count_scene_data, async_delete_scene_data
from monitor.models import MonitorRealTimeInfo, MonitorAlarm

from common.obs_tools import obs_client, object_restore_status, get_job_type
from common.ploto_enum import CommonStatuEnum, DataStatuEnum, DataTypeEnum
from common.ploto_response import PlotoResponse
from common.tools import auth_request, fill_filter_data, set_header


logger = logging.getLogger(__name__)


class SceneDataHandler(APIView):
    """
    单个场景数据对象的操作类  场景数据是文件夹
    """

    @auth_request
    def get(self, request, pk):
        """
        获取单条场景数据的详情
        param request,pk
        return: data + list_tag + playback_url
        1、获取pk对应的数据obj
        2、并获取对应的tag字段信息，返回list_tag，方便前端更新此字段
        3、针对MP4文件获取对应的可授权的播放链接  传回前端播放
        """
        response = PlotoResponse()
        if not request.user.has_perm('data_mgt.view_scenedata'):
            response.code = CommonStatuEnum.AUTHORIZATION_EXCEPTION.code
            response.msg = CommonStatuEnum.AUTHORIZATION_EXCEPTION.msg
            return Response(response.dict, status=status.HTTP_403_FORBIDDEN)
        obj = SceneData.objects.get(id=pk)
        list_tag = obj.tag.split('|')
        if None is re.match(r'^obs://\S+/.+[^/]+$', obj.url):
            logger.warning("场景数据url无效")
            response.code = DataStatuEnum.SCENE_URL_EXCEPTION.code
            response.msg = DataStatuEnum.SCENE_URL_EXCEPTION.msg
            return Response(response.dict, status=status.HTTP_400_BAD_REQUEST)
        object_url = obj.url
        bucket_name = object_url.split("/")[2]
        object_key_path = object_url.split("/", 3)[-1]
        resp = obs_client.listObjects(bucket_name, prefix=object_key_path)
        for content in resp.body.contents:
            if ".mp4" in content.key:
                playback_key = content.key
        try:
            resp = obs_client.createSignedUrl('GET', bucket_name, playback_key, expires=3600)
            serializer = SceneDataSerializer(obj)
            response.data = serializer.data
            response.data["list_tag"] = list_tag
            response.data["playback_url"] = resp.signedUrl
            return Response(response.dict, status=status.HTTP_200_OK)
        except Exception as e:
            logger.error("序列化或调用obs生成链接异常:%s, %s", repr(e), traceback.format_exc())
            response.code = CommonStatuEnum.INTERNAL_ERROR.code
            response.msg = CommonStatuEnum.INTERNAL_ERROR.msg
            return Response(response.dict, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

    def post(self, request):
        """
        场景数据新增接口
        :param request:
        :return: data  新增数据信息
        1、获取request写入的数据信息
        2、保存数据信息，并返回
        """
        response = PlotoResponse()
        params = request.data
        if isinstance(params, dict):
            params = [params]
        data_list = []
        bag_name_list = []
        for data in params:
            try:
                from_data = AnonymizedData.objects.filter(
                    uuid=data.get('from_uuid', ''))[0]
            except Exception as e:
                logger.error("原始数据查询出错:%s, %s", repr(e),
                             traceback.format_exc())
                response.code = CommonStatuEnum.INTERNAL_ERROR.code
                response.msg = CommonStatuEnum.INTERNAL_ERROR.msg
                response.data["Error: "] = repr(e)
                return Response(response.dict, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
            url = data.get("url", "")
            bucket_name = url.split('/')[2]
            object_key = url.split('/', 3)[-1]
            file_list, size = self._get_obs_file(bucket_name, object_key)
            for file in file_list:
                new_object_key = file.split('/', 3)[-1][:-1]
                scenario_id = new_object_key.split(('__'))[1]
                name = new_object_key.split('/', 3)[-1]
                scene_obj = SceneData(name=name,
                                      size=size,
                                      car_id=data.get('car_id', ''),
                                      data_type=data.get('data_type', 0),
                                      region=data.get('region', ''),
                                      url=file,
                                      is_delete=data.get('is_delete', False),
                                      tag=data.get('tag', ''),
                                      remark=data.get('remark', ''),
                                      collect_time=data.get(
                                          'collect_time', datetime.datetime.now()),
                                      storage_type=data.get('storage_type', 0),
                                      scenario_id=scenario_id,
                                      from_data_id=from_data.id,
                                      bucket_name=bucket_name,
                                      object_key=new_object_key)
                data_list.append(scene_obj)
                bag_name_list.append(name)

            async_count_scene_data.delay(scenario_id=data.get('scenario_id', ''),
                                         size=data.get('size', 0),
                                         city=data.get('region', ''))
            try:
                SceneData.objects.bulk_create(data_list)
                SceneCutRecord.objects.filter(bag_id=data.get("bag_id", 0)).update(
                    status=2, bag_name=','.join(bag_name_list))
                # 添加成功后删除任务
                AsyncJobSceneCut.objects.filter(
                    data_id=data.get("bag_id", 0)).delete()
            except Exception as e:
                logger.error("数据库更新失败:%s, %s", repr(e), traceback.format_exc())
                response.code = CommonStatuEnum.INTERNAL_ERROR.code
                response.msg = CommonStatuEnum.INTERNAL_ERROR.msg
                return Response(response.dict, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        response.code = CommonStatuEnum.OK.code
        response.msg = CommonStatuEnum.OK.msg
        return Response(response.dict, status=status.HTTP_200_OK)

    def _get_obs_file(self, bucket_name, object_key):
        obs_list = obs_client.listObjects(bucket_name, object_key)
        obs_list_contents = obs_list.body.contents
        res = []
        size = 0
        # 统计切割出的文件夹目录
        for file in obs_list_contents:
            if file.key == object_key + '/':
                continue
            resp = obs_client.getObjectMetadata(bucket_name, file)
            size += resp.body.contentLength
            if resp.body.contentLength == 0:
                res.append("obs://{}/{}".format(bucket_name, file))
        return res, size

    def put(self, request, pk):
        """
        单条场景数据修改
        :param request
        :param pk
        :return: data  新增数据信息
        1、获取指定id的obj信息
        2、获取修改的操作  针对取回/归档/修改tag/修改其他字段
        2.1 前端传了tag字段，则表示更新了tag
           a、用. join()连接前端传的数据
           b、并更新到对应的数据字段
           c、并将更新的数据返回给前端更新
        2.2 修改其他字段：通过序列化的方式更新修改的字段
        2.3 取回和归档的逻辑参考_retrieval和_save_cold流程
        """
        params = request.data
        response = PlotoResponse()
        # 修改 tag字段
        if not request.user.has_perm('data_mgt.change_scenedata'):
            response.code = CommonStatuEnum.AUTHORIZATION_EXCEPTION.code
            response.msg = CommonStatuEnum.AUTHORIZATION_EXCEPTION.msg
            return Response(response.dict, status=status.HTTP_403_FORBIDDEN)
        tag = params.get('tag', '')
        remark = params.get('remark')
        # 只有tag和remark能更新
        if tag:
            tag_str = "|".join(tag)
            resp = SceneData.objects.filter(id=pk).update(tag=tag_str)
            if resp != 1:
                logger.error("数据库更新tag失败")
                response.code = CommonStatuEnum.INTERNAL_ERROR.code
                response.msg = CommonStatuEnum.INTERNAL_ERROR.msg
                return Response(response.dict, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
        if remark:
            resp = SceneData.objects.filter(id=pk).update(remark=remark)
            if resp != 1:
                logger.error("数据库更新remark失败")
                response.code = CommonStatuEnum.INTERNAL_ERROR.code
                response.msg = CommonStatuEnum.INTERNAL_ERROR.msg
                return Response(response.dict, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
        return Response(response.dict, status=status.HTTP_201_CREATED)

    def delete(self, request, pk):
        response = PlotoResponse()
        if not request.user.has_perm('data_mgt.delete_scenedata'):
            response.code = CommonStatuEnum.AUTHORIZATION_EXCEPTION.code
            response.msg = CommonStatuEnum.AUTHORIZATION_EXCEPTION.msg
            return Response(response.dict, status=status.HTTP_403_FORBIDDEN)
        resp = SceneData.objects.filter(id=pk).update(is_delete=True)
        if resp != 1:
            logger.error("场景数据表更新失败")
            response.code = CommonStatuEnum.INTERNAL_ERROR.code
            response.msg = CommonStatuEnum.INTERNAL_ERROR.msg
            return Response(response.dict, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
        async_delete_scene_data.delay(pk)  # 异步改变SceneDataCount和monitor
        delete_status = do_delete(pk)
        if delete_status is False:
            logger.error("obs数据删除失败")
            response.code = CommonStatuEnum.INTERNAL_ERROR.code
            response.msg = CommonStatuEnum.INTERNAL_ERROR.msg
            return Response(response.dict, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
        return Response(response.dict, status=status.HTTP_204_NO_CONTENT)


class BatchSceneData(APIView):

    @auth_request
    def get(self, request):
        """
        批量场景数据获取
        :param request
        :return: data
        1、获取分页标记信息
        2、指定部分字段为模糊匹配搜索以及按区间搜索的字段
        3、获取根据过滤条件得到的数据信息
        4、序列化分页数据，返回给数据信息给前端
        """
        response = PlotoResponse()
        if not request.user.has_perm('data_mgt.view_scenedata'):
            response.code = CommonStatuEnum.AUTHORIZATION_EXCEPTION.code
            response.msg = CommonStatuEnum.AUTHORIZATION_EXCEPTION.msg
            return Response(response.dict, status=status.HTTP_403_FORBIDDEN)
        # 重写View中的get方法
        params = request.query_params.dict()
        # 获取分页标记信息,默认为1,page_size 默认为10
        page = int(params.get('page', 1))
        page_size = int(params.get('page_size', 10))
        filter_data = fill_filter_data(params)
        scene_data_list = SceneData.objects.filter(**filter_data)
        #  没有数据提示 传空数据出去
        if scene_data_list.count() == 0:
            return Response({'message': 'data is empty!', "data": []}, status=status.HTTP_200_OK)

        paginator = Paginator(scene_data_list, page_size)
        try:
            page_scene_data_list = paginator.page(page)
        except PageNotAnInteger:
            page_scene_data_list = paginator.page(1)
        except EmptyPage:
            page_scene_data_list = paginator.page(paginator.num_pages)
        try:
            scene_data = SceneDataSerializer(instance=page_scene_data_list, many=True)
            response.data["data_list"] = scene_data.data
            response.data["count"] = paginator.count
            return Response(response.dict, status=status.HTTP_200_OK)
        except Exception as e:
            logger.error("序列化异常:%s, %s", repr(e), traceback.format_exc())
            response.code = CommonStatuEnum.INTERNAL_ERROR.code
            response.msg = CommonStatuEnum.INTERNAL_ERROR.msg
            return Response(response.dict, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

    def delete(self, request):
        """
        批量场景数据修改存储类别
        :param request
        :return: data
        1、获取前端传入的ids信息
        2、根据ids调用数据库的filter 和 update操作
        """
        response = PlotoResponse()
        if not request.user.has_perm('data_mgt.delete_scenedata'):
            response.code = CommonStatuEnum.AUTHORIZATION_EXCEPTION.code
            response.msg = CommonStatuEnum.AUTHORIZATION_EXCEPTION.msg
            return Response(response.dict, status=status.HTTP_403_FORBIDDEN)
        id_list = request.POST.get('ids')
        if not id_list:
            response.code = CommonStatuEnum.VALID_EXCEPTION.code
            response.msg = CommonStatuEnum.VALID_EXCEPTION.msg
            return Response(response.dict, status=status.HTTP_400_BAD_REQUEST)
        delete_id_list = id_list.split(',')
        try:
            SceneData.objects.filter(id__in=delete_id_list).update(is_delete=True)
            for delete_id in delete_id_list:
                delete_status = do_delete(delete_id)
                if delete_status is False:
                    logger.error("obs数据删除失败")
                    response.code = CommonStatuEnum.INTERNAL_ERROR.code
                    response.msg = CommonStatuEnum.INTERNAL_ERROR.msg
                    return Response(response.dict, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
            return Response(response.dict, status=status.HTTP_204_NO_CONTENT)
        except Exception as e:
            logger.error("数据库更新失败:%s, %s", repr(e), traceback.format_exc())
            response.code = CommonStatuEnum.INTERNAL_ERROR.code
            response.msg = CommonStatuEnum.INTERNAL_ERROR.msg
            return Response(response.dict, status=status.HTTP_500_INTERNAL_SERVER_ERROR)


class SceneDataFileHandler(APIView):

    @auth_request
    def get(self, request, pk):
        """
        场景数据文件下载
        :param request
        :param pk
        :return: signedUrl
        1、根据id 信息获取数据库中对心的信息 并解析桶名和对象名  获取对象前缀信息
        2、根据前缀信息  调用obs接口所有的对象信息
        3、轮循所有的对象  分别获取对应的可授权的下载链接
        4、 分别将 对象名和下载链接 放到字典中 返回给前端，前端在点击下载后，会弹出一个对话框 列出所有的对象以及下载链接 供选择下载
        5、同步下载信息到下载信息表中
        6、同步表中的下载次数
        """
        response = PlotoResponse()
        if not request.user.has_perm('data_mgt.download_scenedata'):
            response.code = CommonStatuEnum.AUTHORIZATION_EXCEPTION.code
            response.msg = CommonStatuEnum.AUTHORIZATION_EXCEPTION.msg
            return Response(response.dict, status=status.HTTP_403_FORBIDDEN)
        download_dict = {}
        obj = SceneData.objects.filter(id=pk)
        if None is re.match(r'^obs://\S+/.+[^/]+$', obj[0].url):
            logger.warning("场景数据url无效")
            response.code = DataStatuEnum.SCENE_URL_EXCEPTION.code
            response.msg = DataStatuEnum.SCENE_URL_EXCEPTION.msg
            return Response(response.dict, status=status.HTTP_400_BAD_REQUEST)
        bucket_name = obj[0].url.split("/")[2]
        object_key = obj[0].url.split("/")[-1]
        prefix = obj[0].url.split("/", 3)[-1]
        resp = obs_client.listObjects(bucket_name, prefix=prefix)
        # 获取list失败的异常处理
        if resp.status >= 300:
            logger.error("获取场景对象列表失败:%s, %s", resp.errorCode, resp.errorMessage)
            response.msg = DataStatuEnum.GET_SCENE_LIST_EXCEPTION.msg
            response.code = DataStatuEnum.GET_SCENE_LIST_EXCEPTION.code
            return Response(response.dict, status=status.HTTP_400_BAD_REQUEST)
        # 遍历文件夹下的数据
        for content in resp.body.contents:
            # 当前规避：归档存储   恢复中和未恢复  不能下载
            if obj[0].storage_type == 2:
                restore_status = object_restore_status(bucket_name, content.key)
                if restore_status in {'no_restored', 'restoring'}:
                    alarm_details = str(self.request.user) + "用户下载场景数据文件" + object_key + "失败"
                    MonitorAlarm.objects.create(alarm_time=timezone.now(), alarm_content='下载异常',
                                                alarm_details=alarm_details, level=0)
                    response.code = DataStatuEnum.DOWNLOAD_EXCEPTION.code
                    response.msg = DataStatuEnum.DOWNLOAD_EXCEPTION.msg
                    return Response(response.dict, status=status.HTTP_400_BAD_REQUEST)
            headers = SetObjectMetadataHeader()
            headers.contentType = "application/octet-stream"
            # mp4文件默认为播放，需要添加contentType就可以直接下载不播放
            res = obs_client.setObjectMetadata(bucket_name, content.key, headers=headers)
            if res.status >= 300:
                logger.error("设置对象元数据失败:%s, %s", res.errorCode, res.errorMessage)
                response.code = DataStatuEnum.SET_METADATA_EXCEPTION.code
                response.msg = DataStatuEnum.SET_METADATA_EXCEPTION.msg
                return Response(response.dict, status=status.HTTP_400_BAD_REQUEST)
            try:
                resp = obs_client.createSignedUrl('GET', bucket_name, content.key, expires=3600)
            except Exception as e:
                alarm_details = str(self.request.user) + "用户下载场景数据文件" + object_key + "失败"
                try:
                    MonitorAlarm.objects.create(alarm_time=timezone.now(), alarm_content='下载异常',
                                                alarm_details=alarm_details, level=0)
                except Exception as e:
                    logger.error("告警数据表插入数据失败: %s, %s", repr(e), traceback.format_exc())
                logger.error("创建下载url:%s, %s", repr(e), traceback.format_exc())
                response.code = DataStatuEnum.GET_DOWNLOAD_URL_EXCEPTION.code
                response.msg = DataStatuEnum.GET_DOWNLOAD_URL_EXCEPTION.msg
                return Response(response.dict, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
            download_key = content.key.split("/")[-1]
            download_dict[download_key] = resp.signedUrl
        try:
            # 同步下载数据到数据下载信息表中
            DataDownloadInfo.objects.create(data_id=pk, data_type=DataTypeEnum.SCENE_DATA.code,
                                            data_version=obj[0].version, user=self.request.user)
            info_content = str(self.request.user) + "用户下载场景数据文件" + object_key
            MonitorRealTimeInfo.objects.create(create_time=timezone.now(), info_content=info_content)
            # 同步下载次数
            SceneData.objects.filter(id=pk).update(download_times=F('download_times') + 1)
            response.data["data"] = download_dict
            return Response(response.dict, status=status.HTTP_200_OK)
        except Exception as e:
            logger.error("数据下载信息表同步失败:%s, %s", repr(e), traceback.format_exc())
            response.code = CommonStatuEnum.INTERNAL_ERROR.code
            response.msg = CommonStatuEnum.INTERNAL_ERROR.msg
            return Response(response.dict, status=status.HTTP_500_INTERNAL_SERVER_ERROR)


class SceneDataRestore(APIView):
    SceneParam = \
        namedtuple('AnonymizeParam', ['days_num', 'fail_dict', 'obj_list', 'success_object', 'tier'])

    def post(self, request):
        response = PlotoResponse()
        if not request.user.has_perm('data_mgt.restore_anonymizeddata'):
            response.msg = CommonStatuEnum.AUTHORIZATION_EXCEPTION.msg
            response.code = CommonStatuEnum.AUTHORIZATION_EXCEPTION.code
            return Response(response.dict, status=status.HTTP_403_FORBIDDEN)
        params = request.data
        days_num = params.get("days_num", 30)
        rate = params.get("rate", 'STANDARD')
        id_list = params.get('ids', [])
        fail_dict = {}
        success_object = []
        tier = RestoreTier.EXPEDITED if rate == 'EXPEDITED' else RestoreTier.STANDARD
        obj_list = SceneData.objects.filter(id__in=id_list)
        if obj_list is None:
            response.code = DataStatuEnum.NOT_EXIST_EXCEPTION.code
            response.msg = DataStatuEnum.NOT_EXIST_EXCEPTION.msg
            return Response(response.dict, status=status.HTTP_404_NOT_FOUND)
        scene_param = self.SceneParam(days_num=days_num, fail_dict=fail_dict, obj_list=obj_list,
                                      success_object=success_object, tier=tier)
        self._do_restore(scene_param)
        # 对于取回的成功的对象 需要修改其恢复状态  当前设置为恢复中 规避方法下一次操作的时候先查（恢复中->已恢复）
        if success_object:
            if fail_dict:
                response.msg = DataStatuEnum.RESTORE_EXCEPTIOM.msg
                response.code = DataStatuEnum.RESTORE_EXCEPTIOM.code
                response.data = fail_dict
                return Response(response.dict, status=status.HTTP_206_PARTIAL_CONTENT)
            else:
                return Response(response.dict, status=status.HTTP_200_OK)
        else:
            response.msg = DataStatuEnum.RESTORE_EXCEPTIOM.msg
            response.code = DataStatuEnum.RESTORE_EXCEPTIOM.code
            response.data = fail_dict
            return Response(response.dict, status=status.HTTP_400_BAD_REQUEST)

    def _do_restore(self, scene_param):
        days_num, fail_dict, obj_list, success_object, tier = scene_param
        for obj in obj_list:
            result = True
            if None is re.match(r'^obs://\S+/.+[^/]+$', obj.url):
                fail_dict[obj.name] = DataStatuEnum.ANONYMIZE_URL_EXCEPTION.msg
                logger.warning("场景数据%s url无效", obj.name)
                continue
            bucket_name = obj.url.split("/")[2]
            object_key = obj.url.split("/")[-1]
            object_key_path = obj.url.split("/", 3)[-1]
            prefix = '{}/{}'.format(object_key_path, object_key)
            resp = obs_client.listObjects(bucket_name, prefix=prefix)
            # 获取list失败的异常处理
            if resp.status >= 300:
                logger.error("获取场景对象列表失败:%s, %s", resp.errorCode, resp.errorMessage)
                fail_dict[obj.name] = DataStatuEnum.GET_SCENE_LIST_EXCEPTION.msg
                continue
            for content in resp.body.contents:
                restore_status = object_restore_status(bucket_name, content.key)
                if restore_status in {'no_restored', 'restored'}:
                    resp = obs_client.restoreObject(bucket_name, content.key, days_num, tier)
                    if resp.status >= 300:
                        fail_dict[obj.name] = resp.errorMessage
                        result = False
                        break
                    try:
                        ObsAsyncJobStatus.objects.create(job_id=obj.id, job_data_type=1, job_type=get_job_type(tier),
                                                         job_bucket=bucket_name, job_object=content.key,
                                                         next_run_time=timezone.now())
                    except Exception as e:
                        logger.error("异步任务表新增数据失败: %s, %s", repr(e), traceback.format_exc())
                        fail_dict[obj.name] = CommonStatuEnum.INTERNAL_ERROR.msg
                        result = False
                        break
                else:
                    fail_dict[obj.name] = DataStatuEnum.RESTORING_NOT_ALLOWED.msg
                    result = False
                    break
            # 针对结果进行处理
            if result:
                try:
                    # 对于取回的成功的对象 需要修改其恢复状态  当前设置为恢复中 规避方法下一次操作的时候先查（恢复中->已恢复）
                    SceneData.objects.filter(id=obj.id).update(restoration_status=2)
                except Exception as e:
                    logger.error("脱敏数据表更新失败:%s, %s", repr(e), traceback.format_exc())
                    fail_dict[obj.name] = CommonStatuEnum.INTERNAL_ERROR.msg
                success_object.append(obj.name)


class SceneDataModifyStorageClass(APIView):
    SceneParam = \
        namedtuple('AnonymizeParam', ['fail_dict', 'headers', 'obj_list', 'storage_class', 'success_object'])

    def post(self, request):
        response = PlotoResponse()
        if not request.user.has_perm('data_mgt.modify_class_scenedata'):
            response.code = CommonStatuEnum.AUTHORIZATION_EXCEPTION.code
            response.msg = CommonStatuEnum.AUTHORIZATION_EXCEPTION.msg
            return Response(response.dict, status=status.HTTP_403_FORBIDDEN)
        params = request.data
        id_list = params.get('ids', [])
        storage_class = params.get("storage_class")
        headers = SetObjectMetadataHeader()
        set_header(headers, storage_class)
        success_object = []
        fail_dict = {}
        obj_list = SceneData.objects.filter(id__in=id_list)
        if obj_list is None:
            response.code = DataStatuEnum.NOT_EXIST_EXCEPTION.code
            response.msg = DataStatuEnum.NOT_EXIST_EXCEPTION.msg
            return Response(response.dict, status=status.HTTP_404_NOT_FOUND)
        scene_param = self.SceneParam(fail_dict=fail_dict, headers=headers, obj_list=obj_list,
                                      storage_class=storage_class, success_object=success_object)
        self._get_modify_result(scene_param)
        if success_object:
            if fail_dict:
                response.code = DataStatuEnum.MODIFY_CLASS_EXCEPTION.code
                response.msg = DataStatuEnum.MODIFY_CLASS_EXCEPTION.msg
                response.data = fail_dict
                return Response(response.dict, status=status.HTTP_206_PARTIAL_CONTENT)
            else:
                return Response(response.dict, status=status.HTTP_200_OK)
        else:
            response.code = DataStatuEnum.MODIFY_CLASS_EXCEPTION.code
            response.msg = DataStatuEnum.MODIFY_CLASS_EXCEPTION.msg
            response.data = fail_dict
            return Response(response.dict, status=status.HTTP_400_BAD_REQUEST)

    def _get_modify_result(self, scene_param):
        fail_dict, headers, obj_list, storage_class, success_object = scene_param
        for obj in obj_list:
            source_storage_class = obj.storage_type
            result = True
            if None is re.match(r'^obs://\S+/.+[^/]+$', obj.url):
                fail_dict[obj.name] = DataStatuEnum.ANONYMIZE_URL_EXCEPTION.msg
                logger.warning("场景数据数据%s url无效", {obj.name})
                continue
            bucket_name = obj.url.split("/")[2]
            object_key = obj.url.split("/")[-1]
            object_key_path = obj.url.split("/", 3)[-1]
            prefix = '{}/{}'.format(object_key_path, object_key)
            resp = obs_client.listObjects(bucket_name, prefix=prefix)
            # 获取list失败的异常处理
            if resp.status >= 300:
                logger.error("获取场景对象列表失败:%s, %s", resp.errorCode, resp.errorMessage)
                fail_dict[obj.name] = DataStatuEnum.GET_SCENE_LIST_EXCEPTION.msg
                continue
            # 遍历对象目录下的文件
            for content in resp.body.contents:
                # 从归档存储修改为标准存储时 需要判断恢复状态为已恢复  才能操作
                if source_storage_class == 2:
                    restore_status = object_restore_status(bucket_name, content.key)
                    if restore_status == 'restored':
                        resp = obs_client.setObjectMetadata(bucket_name, content.key, headers=headers)
                        if resp.status >= 300:
                            logger.error("设置对象元数据失败:%s, %s", resp.errorCode, resp.errorMessage)
                            fail_dict[obj.name] = DataStatuEnum.SET_METADATA_EXCEPTION.msg
                            result = False
                            break
                    else:
                        fail_dict[obj.name] = DataStatuEnum.MODIFY_CLASS_NOT_ALLOWED.msg
                        result = False
                        break
                else:
                    resp = obs_client.setObjectMetadata(bucket_name, content.key, headers=headers)
                    if resp.status >= 300:
                        logger.error("设置对象元数据失败:%s, %s", resp.errorCode, resp.errorMessage)
                        fail_dict[obj.name] = DataStatuEnum.SET_METADATA_EXCEPTION.msg
                        result = False
                        break
            # 处理场景数据下的对象
            if result:
                try:
                    # 执行成功的id  更新对应的字段  修改为归档存储时 恢复状态为未恢复  修改为其他存储类别 则恢复状态为--
                    if headers.storageClass == 'COLD':
                        SceneData.objects.filter(id=obj.id).update(storage_type=storage_class,
                                                                   restoration_status=1)
                    else:
                        SceneData.objects.filter(id=obj.id).update(storage_type=storage_class,
                                                                   restoration_status=0)
                except Exception as e:
                    logger.error("场景数据表更新失败：%s, %s", repr(e), traceback.format_exc())
                    fail_dict[obj.name] = CommonStatuEnum.INTERNAL_ERROR.msg
                success_object.append(obj.name)


def do_delete(pk):
    obj = SceneData.objects.get(id=pk)
    object_url = obj.url
    bucket_name = object_url.split("/")[2]
    object_key_path = object_url.split("/", 3)[-1]
    resp = obs_client.listObjects(bucket_name, prefix=object_key_path)
    for content in resp.body.contents:
        delete_url = content.key
        delete_resp = obs_client.deleteObject(bucket_name, delete_url)
        if delete_resp.status >= 300:
            return False
    return True


class SceneDataReview(APIView):
    @auth_request
    def post(self, request):
        """
        新增数据集, 导入指定的数据集
        """
        response = PlotoResponse()
        data = request.data
        dataset_id = data.get('dataset_id')
        id = data.get('id')
        scene_obj = SceneData.objects.filter(id=id)[0]
        try:
            import_task = DataImportTask(
                des_dataset_id=dataset_id, import_path=scene_obj.url, import_status="RUNNING")
            import_task.save()
            scene_obj.data_status = True  # 已审核
            scene_obj.save()
            SceneReviewRecord.objects.create(
                scenedata_id=id, check_time=timezone.now(), operator=request.user.username, dataset_id=dataset_id, import_status=1)
            logger.info("Create import task success")
            response.code = CommonStatuEnum.OK.code
            response.msg = CommonStatuEnum.OK.msg
            return Response(response.dict, status=status.HTTP_200_OK)
        except Exception as e:
            logger.error("数据集更新失败:%s, %s", repr(e), traceback.format_exc())
            response.code = CommonStatuEnum.INTERNAL_ERROR.code
            response.msg = CommonStatuEnum.INTERNAL_ERROR.msg
            return Response(response.dict, status=status.HTTP_500_INTERNAL_SERVER_ERROR)


class SceneDataDownloadSource(APIView):
    @auth_request
    def get(self, request, pk):
        response = PlotoResponse()
        if not request.user.has_perm('data_mgt.view_anonymizeddata'):
            response.code = CommonStatuEnum.AUTHORIZATION_EXCEPTION.code
            response.msg = CommonStatuEnum.AUTHORIZATION_EXCEPTION.msg
            response.status = status.HTTP_403_FORBIDDEN
            return Response(response.dict, status=status.HTTP_403_FORBIDDEN)
        response = PlotoResponse()
        obj_list = DataDownloadInfo.objects.filter(data_id=pk, data_type=DataTypeEnum.SCENE_DATA.code)
        try:
            serializer = DataDownloadInfoSerializer(instance=obj_list, many=True)
            for obj in serializer.data:
                user_obj = User.objects.get(pk=obj['user'])
                user_name = user_obj.username
                obj['user'] = user_name
            response.data = serializer.data
            return Response(response.dict, status=status.HTTP_200_OK)
        except Exception as e:
            logger.error("序列化异常:%s, %s", repr(e), traceback.format_exc())
            response.code = CommonStatuEnum.INTERNAL_ERROR.code
            response.msg = CommonStatuEnum.INTERNAL_ERROR.msg
            response.status = status.HTTP_500_INTERNAL_SERVER_ERROR
            return Response(response.dict, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

class SceneReviewRecordHandler(APIView):
    @auth_request
    def get(self, request, pk):
        response = PlotoResponse()
        obj_list = SceneReviewRecord.objects.filter(scenedata_id=pk)
        try:
            serializer = SceneReviewRecordSerializer(instance=obj_list, many=True)
            response.data = serializer.data
            return Response(response.dict, status=status.HTTP_200_OK)
        except Exception as e:
            logger.error("序列化异常:%s, %s", repr(e), traceback.format_exc())
            response.code = CommonStatuEnum.INTERNAL_ERROR.code
            response.msg = CommonStatuEnum.INTERNAL_ERROR.msg
            response.status = status.HTTP_500_INTERNAL_SERVER_ERROR
            return Response(response.dict, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
